package com.zdp.leetcodeMiddle;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/*
* 题目描述：
* 给定一个包含 n 个整数的数组 nums 和一个目标值 target，
* 判断 nums 中是否存在四个元素 a，b，c 和 d ，
* 使得 a + b + c + d 的值与 target 相等？找出所有满足条件且不重复的四元组。
注意：
答案中不可以包含重复的四元组。
示例：
给定数组 nums = [1, 0, -1, 0, -2, 2]，和 target = 0。
满足要求的四元组集合为：
[
  [-1,  0, 0, 1],
  [-2, -1, 1, 2],
  [-2,  0, 0, 2]
]
来源：力扣（LeetCode）
链接：https://leetcode-cn.com/problems/4sum
著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
* */
public class 四数之和_18 {
    public static void main(String[] args) {
        四数之和_18 demo = new 四数之和_18();
        List<List<Integer>> lists = demo.fourSum(new int[]{
                0, 0, 0, 0
        }, 0);
        for(List<Integer> list:lists){
            System.out.println(list.toString());
        }
    }

    /*
    * 解法类似三数之和，用双指针来去掉一层循环
    * */
    public List<List<Integer>> fourSum(int[] nums, int target) {
        // 排序 用排序来保证不重复
        Arrays.sort(nums);
        List<List<Integer>> result = new ArrayList<>();
        int length = nums.length;
        // 因为有四个数，所以要先固定两个数  最后两个数再用双指针来去掉一层循环
        for(int first=0;first<length-3;first++){
            if(first!=0 && nums[first]==nums[first-1]){
                continue;
            }
            // 减少一些循环
            // 如果该数后面的三个数已经大于target 就没有必要循环了
            if(nums[first]+nums[first+1]+nums[first+2]+nums[first+3] > target){
                break;
            }
            // 还能减少，如果该数 加上最大的三个数 就继续
            if(nums[first] + nums[length-1]+ nums[length-2] +  nums[length-3] <target){
                continue;
            }

            // 固定第一个数
            int firstNum = nums[first];
            // 固定第二个数
            for(int second=first+1;second<length-2;second++){
                if( second!=first+1&& nums[second] == nums[second-1]){
                    continue;
                }
                // 也是同理
                if(firstNum + nums[second]+ nums[second+1] + nums[second+2] > target){
                    break;
                }
                if(firstNum + nums[second] + nums[length-1] + nums[length-2] < target){
                    continue;
                }
                int secondNum = nums[second];
                // 利用双指针来获取到最后两个数
                int third = second+1;
                int four = length-1;
                while(third<four){
                    int sum = firstNum + secondNum + nums[third] + nums[four];
                    if(sum == target){
                        result.add(new ArrayList<Integer>(Arrays.asList(firstNum,secondNum,nums[third],nums[four])));
                       // 相等 那还可以 继续看看是否还有相等的
                        while(third<four&&nums[third]==nums[third+1]){
                            third++;
                        }
                        third++;
                        while(third<four&&nums[four]==nums[four-1]){
                            four--;
                        }
                        four--;
                    }else if(sum < target){
                        // sum < target ,左指针向右移动
                        while(third<four && nums[third] == nums[third+1]){
                            third++;
                        }
                        third++;
                    }else{
                        // sum > target , 右指针向左移动
                        while(third<four&&nums[four]==nums[four-1]){
                            four--;
                        }
                        four--;
                    }
                }
            }
        }
        return result;
    }
}
